package climit

import (
	"fmt"
	"os"
	"strconv"
	"strings"
)

type CommandDescriptor interface {
	GetBoolOptionValue(optionName string) bool
	GetIntOptionValue(optionName string, defaultValue int) int
	GetFloat64OptionValue(optionName string, defaultValue float64) float64
	GetStringOptionValue(optionName string, defaultValue string) string
	GetAllOptions() map[string]string
	GetExpressionValue(expressionName string) (string, bool)
	GetAllExpressions() map[string]string
	GetArguments() []string
	GetCommandName() string
	HasArguments() bool
	HasOptions() bool
	HasExpressions() bool
	GetWorkDirectory() string
	GetSuperCommandDescriptor() CommandDescriptor
	setSuperCommandDescriptor(superCommandDescriptor CommandDescriptor)
	execute()
}

type defaultCommandDescriptor struct {
	definition             *commandDefinition
	commandPath            string
	commandName            string
	optionValues           map[string]string
	expressionValues       map[string]string
	arguments              []string
	superCommandDescriptor CommandDescriptor
}

func (receiver *defaultCommandDescriptor) execute() {
	receiver.definition.run(receiver)
}

func (receiver *defaultCommandDescriptor) setSuperCommandDescriptor(superCommandDescriptor CommandDescriptor) {
	receiver.superCommandDescriptor = superCommandDescriptor
}

func (receiver *defaultCommandDescriptor) GetSuperCommandDescriptor() CommandDescriptor {
	return receiver.superCommandDescriptor
}

func (receiver *defaultCommandDescriptor) setOptionValue(optionName string, optionValue string) {
	receiver.optionValues[optionName] = optionValue
	optionDefinition, exist := receiver.definition.getOptionDefinition(optionName)
	if !exist {
		return
	}
	// 如果设置的是长选项的值，如果长选项设置的对应的短选项，那么短选项的值也需要同步设置
	if len(optionName) > 1 && len(optionDefinition.getShortName()) > 0 {
		receiver.optionValues[optionDefinition.getShortName()] = optionValue
	}
	// 如果设置的是短选项的值，如果短选项设置的对应的长选项，那么长选项的值也需要同步设置
	if len(optionName) == 1 && len(optionDefinition.getLongName()) > 0 {
		receiver.optionValues[optionDefinition.getLongName()] = optionValue
	}
}

func (receiver *defaultCommandDescriptor) GetBoolOptionValue(optionName string) bool {
	receiver.checkIsBooleanOption(optionName)
	_, exist := receiver.optionValues[optionName]
	return exist
}

func (receiver *defaultCommandDescriptor) GetIntOptionValue(optionName string, defaultValue int) int {
	receiver.checkIsNonBooleanOption(optionName)
	strOptionValue, exist := receiver.optionValues[optionName]
	if !exist {
		return defaultValue
	} else {
		optionValue, err := strconv.Atoi(strOptionValue)
		if err != nil {
			fmt.Printf("%s -> Option %s's value must be an integer value.\n", receiver.commandPath, GetDisplayOptionName(optionName))
			os.Exit(3)
		}
		return optionValue
	}
}

func (receiver *defaultCommandDescriptor) GetFloat64OptionValue(optionName string, defaultValue float64) float64 {
	receiver.checkIsNonBooleanOption(optionName)
	strOptionValue, exist := receiver.optionValues[optionName]
	if !exist {
		return defaultValue
	} else {
		optionValue, err := strconv.ParseFloat(strOptionValue, 64)
		if err != nil {
			fmt.Printf("%s -> Option %s's value must be an float value.\n", receiver.commandPath, GetDisplayOptionName(optionName))
			os.Exit(3)
		}
		return optionValue
	}
}

func (receiver *defaultCommandDescriptor) GetStringOptionValue(optionName string, defaultValue string) string {
	receiver.checkIsNonBooleanOption(optionName)
	strOptionValue, exist := receiver.optionValues[optionName]
	if !exist {
		return defaultValue
	} else {
		return strOptionValue
	}
}

func (receiver *defaultCommandDescriptor) GetAllOptions() map[string]string {
	return receiver.optionValues
}

func (receiver *defaultCommandDescriptor) GetAllExpressions() map[string]string {
	return receiver.expressionValues
}

func (receiver *defaultCommandDescriptor) GetExpressionValue(expressionName string) (string, bool) {
	receiver.checkIsExpression(expressionName)
	expressionValue, exist := receiver.expressionValues[expressionName]
	return expressionValue, exist
}

func (receiver *defaultCommandDescriptor) GetArguments() []string {
	return receiver.arguments
}

func (receiver *defaultCommandDescriptor) GetCommandName() string {
	return receiver.commandName
}

func (receiver *defaultCommandDescriptor) HasArguments() bool {
	return len(receiver.arguments) > 0
}

func (receiver *defaultCommandDescriptor) HasOptions() bool {
	return len(receiver.optionValues) > 0
}

func (receiver *defaultCommandDescriptor) HasExpressions() bool {
	return len(receiver.expressionValues) > 0
}

func (receiver *defaultCommandDescriptor) GetWorkDirectory() string {
	workdir, _ := os.Getwd()
	return workdir
}

func (receiver *defaultCommandDescriptor) checkIsBooleanOption(optionName string) {
	// 长度大于1的情况下可能是选项名称也可能是表达式的名称，我们的规则里面表达式优先，所以需要先确定一下是否是表达式
	if len(optionName) > 1 {
		if _, exist := receiver.definition.getExpressionDefinition(optionName); exist {
			fmt.Printf("%s -> %s is an expression, not an option", receiver.commandPath, GetDisplayOptionName(optionName))
			os.Exit(3)
		}
	}
	optionDefinition, exist := receiver.definition.getOptionDefinition(optionName)
	if !exist && !optionDefinition.isBool() {
		fmt.Printf("%s -> %s is a non-boolean option", receiver.commandPath, GetDisplayOptionName(optionName))
		os.Exit(3)
	}
}

func (receiver *defaultCommandDescriptor) checkIsNonBooleanOption(optionName string) {

	// 长度大于1的情况下可能是选项名称也可能是表达式的名称，我们的规则里面表达式优先，所以需要先确定一下是否是表达式
	if len(optionName) > 1 {
		if _, exist := receiver.definition.getExpressionDefinition(optionName); exist {
			fmt.Printf("%s -> %s is an expression, not an option", receiver.commandPath, GetDisplayOptionName(optionName))
			os.Exit(3)
		}
	}
	optionDefinition, exist := receiver.definition.getOptionDefinition(optionName)
	if exist && optionDefinition.isBool() {
		fmt.Printf("%s -> %s is a boolean option", receiver.commandPath, GetDisplayOptionName(optionName))
		os.Exit(3)
	}
}

func (receiver *defaultCommandDescriptor) checkIsExpression(expressionName string) {
	if len(expressionName) < 1 {
		return
	}
	if _, exist := receiver.definition.getExpressionDefinition(expressionName); !exist {
		fmt.Printf("%s -> %s is not an expression", receiver.commandPath, GetDisplayExpressionName(expressionName))
		os.Exit(3)
	}
}

func parseCommandDefinition(args []string, definition *commandDefinition, commandPathRoot string) CommandDescriptor {
	var commandToExecute CommandDescriptor = nil
	osArguments := args[1:]
	commandName := args[0]
	commandPath := commandPathRoot + "-" + commandName
	if commandPathRoot == "" {
		commandPath = commandName
	}
	commandDescriptor := &defaultCommandDescriptor{
		definition:       definition,
		commandPath:      commandPath,
		commandName:      commandName,
		optionValues:     make(map[string]string),
		expressionValues: make(map[string]string),
		arguments:        make([]string, 0),
	}

	if len(osArguments) == 0 {
		return commandDescriptor
	}

	commandToExecute = commandDescriptor

	skip := false
	for i, arg := range osArguments {
		if skip {
			skip = false
			continue
		}
		// 如果是 -- ,则代表剩余的arg全是参数，直接放到参数列表里返回
		if arg == "--" {
			for subI := i + 1; subI < len(osArguments); subI++ {
				subArg := osArguments[subI]
				commandDescriptor.arguments = append(commandDescriptor.arguments, subArg)
			}
			break
		}
		nextArg := ""
		if i+1 < len(osArguments) {
			nextArg = osArguments[i+1]
		}
		// 如果是一个单独的 -，则下一个arg是一个参数，直接放入参数列表中，继续循环
		if arg == "-" {
			skip = true
			if nextArg != "" {
				commandDescriptor.arguments = append(commandDescriptor.arguments, nextArg)
			}
			continue
		}
		if parsed, nextArgUsed := tryParseExpression(commandDescriptor, arg, nextArg); parsed {
			skip = nextArgUsed
			continue
		}
		if parsed, nextArgUsed := tryParseShortOptions(commandDescriptor, arg, nextArg); parsed {
			skip = nextArgUsed
			continue
		}
		if parsed, nextArgUsed := tryParseLongOption(commandDescriptor, arg, nextArg); parsed {
			skip = nextArgUsed
			continue
		}
		// 如果是子命令
		if subCommandDefinition, exist := definition.getSubCommandDefinition(arg); exist {
			subCommandDescriptor := parseCommandDefinition(osArguments[i:], subCommandDefinition, commandPath)
			commandToExecute = subCommandDescriptor
			subCommandDescriptor.setSuperCommandDescriptor(subCommandDescriptor)
			break
		}
		commandDescriptor.arguments = append(commandDescriptor.arguments, arg)
	}
	hasErr := false
	hasErr = checkUnrecognizedOptions(definition, commandDescriptor, commandPath)
	hasErr = hasErr || checkOptionValue(definition, commandDescriptor, commandPath)
	hasErr = hasErr || checkExpressionValue(definition, commandDescriptor, commandPath)
	if hasErr {
		os.Exit(2)
	}

	return commandToExecute
}

// 尝试将参数解析为表达式
// 返回(bool,bool)，第一个值代表是否解析成功，第二个值代表nextArg是否使用了
func tryParseExpression(commandDescriptor *defaultCommandDescriptor, arg string, nextArg string) (bool, bool) {
	nextArgUsed := false
	// 如果不是 - 打头，意味着不是表达式或者短选项，直接返回
	if !strings.HasPrefix(arg, "-") {
		return false, false
	} else if strings.HasPrefix(arg, "--") { // 如果是以 -- 打头，意味这引导的是长参数或是剩余参数列表，直接返回
		return false, false
	}
	expression := arg[1:]
	expressionName := expression
	expressionValue := ""
	if strings.Contains(expression, "=") {
		expressionNameValue := strings.SplitN(expression, "=", 2)
		expressionName = expressionNameValue[0]
		expressionValue = expressionNameValue[1]
	} else {
		expressionValue = nextArg
		nextArgUsed = true
	}
	if len(expressionName) < 2 {
		// 表达式的名称长度至少是2，如果是解析出来名称长度是1的话，就可以直接返回了
		return false, false
	}
	_, exist := commandDescriptor.definition.getExpressionDefinition(expressionName)
	if !exist {
		return false, false
	} else {
		commandDescriptor.expressionValues[expressionName] = expressionValue
		return true, nextArgUsed
	}

}

func tryParseShortOptions(commandDescriptor *defaultCommandDescriptor, arg string, nextArg string) (bool, bool) {
	nextArgUsed := false
	// 如果不是 - 打头，意味着不是表达式或者短选项，直接返回
	if !strings.HasPrefix(arg, "-") {
		return false, false
	} else if strings.HasPrefix(arg, "--") { // 如果是以 -- 打头，意味这引导的是长参数或是剩余参数列表，直接返回
		return false, false
	}
	for i, char := range arg[1:] {
		optionName := string(char)
		optionValue := ""
		optionDefinition, exist := commandDescriptor.definition.getOptionDefinition(optionName)
		// 如果选项定义存在，并且是一个非bool类型的参数，那么解析其参数值
		if exist && !optionDefinition.isBool() {
			if i+1 < len(arg)-1 {
				optionValue = arg[i+2:]
			} else {
				optionValue = nextArg
				nextArgUsed = true
			}
			commandDescriptor.setOptionValue(optionName, optionValue)
			break
		} else {
			commandDescriptor.setOptionValue(optionName, "")
		}
	}
	return true, nextArgUsed
}

func tryParseLongOption(commandDescriptor *defaultCommandDescriptor, arg string, nextArg string) (bool, bool) {
	// 如果不是 -- 打头，证明不是长参数，直接返回
	if !strings.HasPrefix(arg, "--") {
		return false, false
	}
	nextArgUsed := false
	optionName := arg[2:]
	optionValue := ""
	// 如果是用 --optionName=optionValue 的方式为长选项提供参数
	if strings.Contains(optionName, "=") {
		optionNameValue := strings.SplitN(optionName, "=", 2)
		optionName = optionNameValue[0]
		optionValue = optionNameValue[1]
	} else {
		optionValue = nextArg
		nextArgUsed = true
	}
	commandDescriptor.setOptionValue(optionName, optionValue)
	return true, nextArgUsed
}

// 检测是否有未知的选项
func checkUnrecognizedOptions(definition *commandDefinition, commandDescriptor *defaultCommandDescriptor, commandPath string) (hasUnrecognizedOptions bool) {
	if len(commandDescriptor.optionValues) == 0 {
		return false
	}
	recognizedOptionSet := definition.getAllOptionNameSet()
	unrecognizedOptions := make([]string, 0)
	for optionName := range commandDescriptor.optionValues {
		if !recognizedOptionSet.Contains(optionName) {
			unrecognizedOptions = append(unrecognizedOptions, optionName)
		}
	}

	if len(unrecognizedOptions) > 0 {
		fmt.Print(commandPath + " -> unrecognized options: ")
		for _, name := range unrecognizedOptions {
			fmt.Print(GetDisplayOptionName(name), "")
		}
		fmt.Println()
		hasUnrecognizedOptions = true
	}
	return hasUnrecognizedOptions
}

// 检测选项值是否传递了
func checkOptionValue(definition *commandDefinition, commandDescriptor *defaultCommandDescriptor, commandPath string) (hasValueErr bool) {
	allOptionDefinition := definition.getAllOptionDefinition()
	var optionValue string
	hasValueErr = false
	for _, optionDefinition := range allOptionDefinition {
		if len(optionDefinition.getShortName()) > 0 {
			optionValue = commandDescriptor.optionValues[optionDefinition.getShortName()]
		} else {
			optionValue = commandDescriptor.optionValues[optionDefinition.getLongName()]
		}
		if optionDefinition.isRequired() && len(optionValue) == 0 {
			println(commandPath + " 必须选项(shortName=" + optionDefinition.getShortName() + ",LongName=" + optionDefinition.getShortName() + ")没有提选项值")
		}
		errMsg, ok := optionDefinition.checkValue(optionValue)
		hasValueErr = hasValueErr || !ok
		if !ok {
			println(commandPath + " " + errMsg)
		}
	}
	return hasValueErr
}

// 检测选项值是否传递了
func checkExpressionValue(definition *commandDefinition, commandDescriptor *defaultCommandDescriptor, commandPath string) (hasValueErr bool) {
	allExpressionDefinitions := definition.getAllExpressionDefinition()
	hasValueErr = false
	for _, expressionDefinition := range allExpressionDefinitions {
		expressionValue := commandDescriptor.expressionValues[expressionDefinition.getName()]
		if expressionDefinition.isRequired() && len(expressionValue) == 0 {
			println(commandPath + " 必须表达式(expressionName=" + expressionDefinition.getName() + ")没有提供值")
		}
		errMsg, ok := expressionDefinition.checkValue(expressionValue)
		hasValueErr = hasValueErr || !ok
		if !ok {
			println(commandPath + " " + errMsg)
		}
	}
	return hasValueErr
}

func GetDisplayOptionName(optionName string) string {
	if len(optionName) > 1 {
		return "[--" + optionName + "]"
	} else {
		return "[-" + optionName + "]"
	}
}

func GetDisplayExpressionName(expressionName string) string {
	return "[-" + expressionName + "]"
}
